﻿///  Copyright (c) 2009 Ray Liang (http://www.dotnetage.com)
///  Dual licensed under the MIT and GPL licenses:
///  http://www.opensource.org/licenses/mit-license.php
///  http://www.gnu.org/licenses/gpl.html
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using ICSharpCode.SharpZipLib.Zip;
using System.IO;
using System.Collections.Specialized;

namespace DNA.IO.Compress
{
    public class ZipExtract : IDisposable
    {
        private string _password;
        private Stream sourceStream;
        //private ZipInputStream zipStream;

        public string Password
        {
            get { return _password; }
            set { _password = value; }
        }

        public ZipExtract(Stream source)
        {
            sourceStream = source;
        }

        public ZipExtract(string compressFile)
        {
            sourceStream = File.Open(compressFile, FileMode.Open);
        }

        /// <summary>
        /// 从压缩包内将指定文件以文本方式读出
        /// </summary>
        public string ReadFileAsText(string sourceFile)
        {
            MemoryStream memory = new MemoryStream();
            ExtractSingleFile(sourceFile, memory);
            StreamReader reader = new StreamReader(memory);
            string text = reader.ReadToEnd();
            reader.Close();
            memory.Close();
            return text;
        }

        public void ExtractSingleFile(string sourceFile, string fileName)
        {
            FileStream file = File.Create(fileName);
            ExtractSingleFile(sourceFile, file);
            file.Flush();
            file.Close();
        }

        public void ExtractSingleFile(string sourceFile, Stream stream)
        {
            var shadow = new MemoryStream();
            IOHelper.CopyStream(sourceStream, shadow);
            var zipStream = new ZipInputStream(shadow);

            ZipEntry zipFile = zipStream.GetNextEntry();
            while (zipFile != null)
            {
                if (zipFile.IsDirectory)
                {
                    zipFile = zipStream.GetNextEntry();
                    continue;
                }

                if (!string.IsNullOrEmpty(zipFile.Name) && (!string.IsNullOrEmpty(sourceFile)))
                {
                    var zipFileName=zipFile.Name.ToLower().Trim() ;
                    if (zipFileName.Equals(sourceFile.ToLower().Replace("\\", "/")) || zipFileName.Equals(sourceFile.ToLower().Replace("/", "\\")))
                    {
                        IOHelper.CopyStream(zipStream, stream);
                        break;
                    }
                }

                zipFile = zipStream.GetNextEntry();
            }
            zipStream.Close();
            shadow.Close();
        }

        /// <summary>
        /// Extract the zip file to the specified destination path.
        /// </summary>
        /// <param name="destination">目标路径位置,如果不存在则创建目录</param>
        public void ExtractTo(string destination)
        {
            ExtractTo(string.Empty, destination);
        }

        public void ExtractTo(string[] files, string destination)
        {
            var shadow = new MemoryStream();
            IOHelper.CopyStream(sourceStream, shadow);
            var zipStream = new ZipInputStream(shadow);

            if (!String.IsNullOrEmpty(_password))
                zipStream.Password = _password;

            ZipEntry unzipFile = zipStream.GetNextEntry();

            while (unzipFile != null)
            {
                if (files.Contains(unzipFile.Name))
                {
                    string path = destination + unzipFile.Name;
                    if (!Directory.Exists(destination))
                        Directory.CreateDirectory(destination);

                    if (unzipFile.IsDirectory)
                        Directory.CreateDirectory(path);
                    else
                    {
                        var fileStream = System.IO.File.Create(path);
                        int size = 2048;
                        byte[] data = new byte[2048];
                        while (true)
                        {
                            size = zipStream.Read(data, 0, data.Length);
                            if (size > 0)
                            {
                                fileStream.Write(data, 0, size);
                            }
                            else
                            {
                                break;
                            }
                        }
                        fileStream.Close();
                    }
                }
                unzipFile = zipStream.GetNextEntry();
            }
            zipStream.Close();
            shadow.Close();
        }

        public void ExtractTo(string sourcePath, string destination)
        {
            var shadow = new MemoryStream();
            IOHelper.CopyStream(sourceStream, shadow);
            var zipStream = new ZipInputStream(shadow);

            if (!String.IsNullOrEmpty(_password))
                zipStream.Password = _password;
            ZipEntry unzipFile = zipStream.GetNextEntry();

            while (unzipFile != null)
            {
                if (!string.IsNullOrEmpty(sourcePath))
                {
                    if (!unzipFile.Name.StartsWith(sourcePath, StringComparison.OrdinalIgnoreCase))
                    {
                        unzipFile = zipStream.GetNextEntry();
                        continue;
                    }
                }

                string path = destination + unzipFile.Name;
                var _dir = Path.GetDirectoryName(path);
                if (!Directory.Exists(_dir))
                    Directory.CreateDirectory(_dir);

                if (unzipFile.IsDirectory)
                {
                    if (!Directory.Exists(path))
                        Directory.CreateDirectory(path);
                }
                else
                {
                    var fileStream = System.IO.File.Create(path);
                    int size = 2048;
                    byte[] data = new byte[2048];
                    while (true)
                    {
                        size = zipStream.Read(data, 0, data.Length);
                        if (size > 0)
                        {
                            fileStream.Write(data, 0, size);
                        }
                        else
                        {
                            break;
                        }
                    }
                    fileStream.Close();
                }
                unzipFile = zipStream.GetNextEntry();
            }
            zipStream.Close();
            shadow.Close();
        }

        public IEnumerable<ZipEntry> Read()
        {
            var files = new List<ZipEntry>();
            var shadow = new MemoryStream();
            IOHelper.CopyStream(sourceStream, shadow);
            var zipStream = new ZipInputStream(shadow);

            ZipEntry zipFile = zipStream.GetNextEntry();
            while (zipFile != null)
            {
                files.Add(zipFile);
                zipFile = zipStream.GetNextEntry();
            }
            zipStream.Close();
            shadow.Close();
            return files;
        }

        public string[] ReadFiles()
        {
            StringCollection files = new StringCollection();
            var shadow = new MemoryStream();
            IOHelper.CopyStream(sourceStream, shadow);
            var zipStream = new ZipInputStream(shadow);

            ZipEntry zipFile = zipStream.GetNextEntry();
            while (zipFile != null)
            {
                files.Add(zipFile.Name);
                zipFile = zipStream.GetNextEntry();
            }
            zipStream.Close();
            shadow.Close();
            //sourceStream.Close();
            string[] result = new string[files.Count];
            files.CopyTo(result, 0);
            //sourceStream.Position = 0;
            return result;
        }


        #region IDisposable 成员

        public void Dispose()
        {
            sourceStream.Close();
            //zipStream.Close();
        }

        #endregion
    }
}
